package com.zhtools.wx.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.zhtools.json.JsonWrapper;
import com.zhtools.utils.HttpClientUtils;
import com.zhtools.wx.WXConfig;
import com.zhtools.wx.WXService;
import com.zhtools.wx.bean.GetAccessTokenByCodeResponse;
import com.zhtools.wx.bean.TemplateData;
import com.zhtools.wx.bean.WXShareResponse;
import com.zhtools.wx.bean.WXUserInfoResponse;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URLEncoder;
import java.util.Map;

/**
 * @author zhanghao
 * 微信服务接口部分实现
 */
public abstract class AbstractWXService implements WXService {
    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractWXService.class);
    private static final String GET_ACCESS_TOKEN_BYCODE_INFO = ">>> 根据Code:{}获取access_token结果:{}";
    private static final String GET_USER_INFO_INFO = ">>> 获取微信账户基本信息结果:{}";
    private static final String GLOBAL_ACCESS_TOKEN_RESPONSE = ">>> 获取全局accessToken接口响应:{}";
    private static final String GLOBAL_JSAPI_TICKET_RESPONSE = ">>> 获取全局jsapi_ticket接口响应:{}";
    private static final String TEMPLATEMESSAGE_SEND_DATA = ">>> 发送模板消息内容:{}";
    private static final String TEMPLATEMESSAGE_RESPONSE = ">>> 发送模板消息接口响应:{}";

    //网页授权接口地址
    private static final String WEB_PAGE_AUTHORIZE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize" +
            "?appid={APPID}" +
            "&redirect_uri={REDIRECT_URI}" +
            "&response_type=code" +
            "&scope=snsapi_userinfo" +
            "&state={STATE}#wechat_redirect";
    //获取AccessToken接口地址
    private static final String ACCESS_TOKEN_API = "https://api.weixin.qq.com/sns/oauth2/access_token" +
            "?appid={APPID}" +
            "&secret={SECRET}" +
            "&code={CODE}" +
            "&grant_type=authorization_code";
    //获取微信你用户详细信息接口地址
    private static final String USER_INFO_API = "https://api.weixin.qq.com/sns/userinfo" +
            "?access_token={ACCESS_TOKEN}" +
            "&openid={OPENID}" +
            "&lang=zh_CN";
    //获取全局AccessToken接口
    private static final String GLOBAL_ACCESS_TOKEN_API = "https://api.weixin.qq.com/cgi-bin/token" +
            "?grant_type=client_credential" +
            "&appid={APPID}" +
            "&secret={SECRET}";
    //获取jsapi票据接口
    private static final String JSAPI_TICKET_API = "https://api.weixin.qq.com/cgi-bin/ticket/getticket" +
            "?access_token={accessToken}" +
            "&type=jsapi";
    //模板消息接口地址
    private static final String TEMPLATE_MESSAGE_API = "https://api.weixin.qq.com/cgi-bin/message/template/send" +
            "?access_token={accessToken}";

    /**
     * 获取微信配置信息
     */
    protected abstract WXConfig getWxConfig();

    /**
     * 从缓存中获取全局 accessToken
     */
    protected abstract String getGlobalAccessTokenFromCache();

    /**
     * 设置全局 accessToken 到缓存中
     */
    protected abstract void setGlobalAccessTokenInCache(String globalAccessToken);

    /**
     * 从缓存中获取全局票据
     */
    protected abstract String getGlobalJsapiTicketFromCache();

    /**
     * 设置全局 jsapiTicket 到缓存中
     *
     * @param jsapiTicket
     */
    protected abstract void setGlobalJsapiTicketInCache(String jsapiTicket);

    @Override
    public final String getWebPageAuthUrl(String redirectUri, String state) {
        String encodeUrl = "";
        try {
            encodeUrl = URLEncoder.encode(redirectUri, "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return WEB_PAGE_AUTHORIZE_URL
                .replace("{APPID}", getWxConfig().getAppId())
                .replace("{REDIRECT_URI}", encodeUrl)
                .replace("{STATE}", state);
    }

    @Override
    public final GetAccessTokenByCodeResponse getAccessTokenByCode(String code) {
        String url = ACCESS_TOKEN_API
                .replace("{APPID}", getWxConfig().getAppId())
                .replace("{SECRET}", getWxConfig().getAppSecret())
                .replace("{CODE}", code);
        String result = null;
        try {
            result = HttpClientUtils.doGet(url);
            LOGGER.info(GET_ACCESS_TOKEN_BYCODE_INFO, code, result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (StringUtils.isEmpty(result)) {
            return null;
        }
        JsonNode node = JsonWrapper.getNode(result);
        boolean occurError = node.has("errcode");
        if (occurError) {
            return null;
        }
        String access_token = node.get("access_token").asText();
        String open_id = node.get("openid").asText();
        return new GetAccessTokenByCodeResponse(access_token, open_id);
    }

    @Override
    public final WXUserInfoResponse getUserInfo(GetAccessTokenByCodeResponse accessToken) {
        String url = USER_INFO_API
                .replace("{ACCESS_TOKEN}", accessToken.getToken())
                .replace("{OPENID}", accessToken.getOpenId());
        String result = null;
        try {
            result = HttpClientUtils.doGet(url);
            LOGGER.info(GET_USER_INFO_INFO, result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (StringUtils.isEmpty(result)) {
            return null;
        }
        JsonNode node = JsonWrapper.getNode(result);
        boolean occurError = node.has("errcode");
        if (occurError) {
            return null;
        }
        String openid = node.get("openid").asText();
        String nickname = node.get("nickname").asText();
        String sex = node.get("sex").asText();
        String headimgurl = node.get("headimgurl").asText();

        WXUserInfoResponse response = new WXUserInfoResponse();
        response.setOpenId(openid);
        response.setNickName(nickname);
        response.setHeadImgUrl(headimgurl);
        response.setSex(sex);
        return response;
    }

    @Override
    public final String getGlobalAccessToken() {
        String accessToken = getGlobalAccessTokenFromCache();
        if (StringUtils.isEmpty(accessToken)) {
            String url = GLOBAL_ACCESS_TOKEN_API.replace("{APPID}", getWxConfig().getAppId()).replace("{SECRET}", getWxConfig().getAppSecret());
            try {
                LOGGER.info(">>> 获取全局accessToken接口请求地址:{}", url);
                String response = HttpClientUtils.doGet(url);
                LOGGER.info(GLOBAL_ACCESS_TOKEN_RESPONSE, response);
                Map<String, Object> map = JsonWrapper.readValue(response, Map.class);
                if (map.containsKey("access_token")) {
                    accessToken = (String) map.get("access_token");
                    setGlobalAccessTokenInCache(accessToken);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return accessToken;
    }

    @Override
    public final String getGlobalJsapiTicket() {
        String ticket = getGlobalJsapiTicketFromCache();
        if (StringUtils.isEmpty(ticket)) {
            String access_token = getGlobalAccessToken();
            if (StringUtils.isNotEmpty(access_token)) {
                String url = JSAPI_TICKET_API.replace("{accessToken}", access_token);
                try {
                    String response = HttpClientUtils.doGet(url);
                    LOGGER.info(GLOBAL_JSAPI_TICKET_RESPONSE, response);
                    Map<String, Object> map = JsonWrapper.readValue(response, Map.class);
                    if (map.containsKey("ticket")) {
                        ticket = (String) map.get("ticket");
                        setGlobalJsapiTicketInCache(ticket);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return ticket;
    }

    @Override
    public final void sendTemplateMessage(TemplateData data) {
        String accessToken = getGlobalAccessToken();
        String url = TEMPLATE_MESSAGE_API.replace("{accessToken}", accessToken);
        String jsonResult = null;
        try {
            String sendData = data.build();
            LOGGER.info(TEMPLATEMESSAGE_SEND_DATA, sendData);
            jsonResult = HttpClientUtils.doPost(url, sendData, null);
            LOGGER.info(TEMPLATEMESSAGE_RESPONSE, jsonResult);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public final WXShareResponse share(String url, String defaultShareImage) {
        return new WXShareResponse(url, getWxConfig().getAppId(), getGlobalJsapiTicket(), defaultShareImage);
    }
}
